﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace Cleware.Debit3.Tools.EdmUtil
{
    public abstract class EdmUtilBOBase
    {
        public abstract XElement GenerateXElement(XNamespace ns);
    }
    public class Function : EdmUtilBOBase
    {
        public class Parameter : EdmUtilBOBase
        {
            public string Name { get; set; }
            public string Type { get; set; }
            public string Mode { get; set; }
            public string MaxLength { get; set; }
            public string Precision { get; set; }
            public string Scale { get; set; }

            public Parameter()
            {
            }

            public override XElement GenerateXElement(XNamespace ns)
            {
                XElement retval;
                XName xname = ns == null ? this.GetType().Name : ns + this.GetType().Name;
                retval = new XElement(xname);
                if (!String.IsNullOrEmpty(Name)) retval.Add(new XAttribute("Name", Name));
                if (!String.IsNullOrEmpty(Type)) retval.Add(new XAttribute("Type", Type));
                if (!String.IsNullOrEmpty(Mode)) retval.Add(new XAttribute("Mode", Mode));
                if (!String.IsNullOrEmpty(MaxLength)) retval.Add(new XAttribute("MaxLength", MaxLength));
                if (!String.IsNullOrEmpty(Precision)) retval.Add(new XAttribute("Precision", Precision));
                if (!String.IsNullOrEmpty(Scale)) retval.Add(new XAttribute("Scale", Scale));
                return retval;
            }
        }
        
        public string Name { get; set; }
        public string ReturnType { get; set; }
        public string Aggregate { get; set; }
        public string BuiltIn { get; set; }
        public string StoreFunctionName { get; set; }
        public string NiladicFunction { get; set; }
        public string IsComposable { get; set; }
        public string ParameterTypeSemantics { get; set; }
        public string Schema { get; set; }
        public List<Parameter> Parameters { get; set; }

        public Function()
        {
            Parameters = new List<Parameter>();
        }
        public override XElement GenerateXElement(XNamespace ns)
        {
            XElement retval;
            XName xname = ns == null ? this.GetType().Name : ns + this.GetType().Name;
            retval = new XElement(xname);
            if (!String.IsNullOrEmpty(Name)) retval.Add(new XAttribute("Name", Name));
            if (!String.IsNullOrEmpty(ReturnType)) retval.Add(new XAttribute("ReturnType", ReturnType));
            if (!String.IsNullOrEmpty(Aggregate)) retval.Add(new XAttribute("Aggregate", Aggregate));
            if (!String.IsNullOrEmpty(BuiltIn)) retval.Add(new XAttribute("BuiltIn", BuiltIn));
            if (!String.IsNullOrEmpty(StoreFunctionName)) retval.Add(new XAttribute("StoreFunctionName", StoreFunctionName));
            if (!String.IsNullOrEmpty(NiladicFunction)) retval.Add(new XAttribute("NiladicFunction", NiladicFunction));
            if (!String.IsNullOrEmpty(IsComposable)) retval.Add(new XAttribute("IsComposable", IsComposable));
            if (!String.IsNullOrEmpty(ParameterTypeSemantics)) retval.Add(new XAttribute("ParameterTypeSemantics", ParameterTypeSemantics));
            if (!String.IsNullOrEmpty(Schema)) retval.Add(new XAttribute("Schema", Schema));

            foreach (Parameter parameter in Parameters)
            {
                retval.Add(parameter.GenerateXElement(ns));
            }
            return retval;
        }
    }

    public class FunctionImport : EdmUtilBOBase
    {
        public class Parameter : EdmUtilBOBase
        {
            public string Name { get; set; }
            public string Mode { get; set; }
            public string Type { get; set; }

            public override XElement GenerateXElement(XNamespace ns)
            {
                XElement retval;
                XName xname = ns == null ? this.GetType().Name : ns + this.GetType().Name;
                retval = new XElement(xname);
                retval.Add(new XAttribute("Name", Name));
                retval.Add(new XAttribute("Mode", Mode));
                retval.Add(new XAttribute("Type", Type));

                return retval;
            }
        }

        public string Name { get; set; }
        public string ReturnType { get; set; }
        public List<Parameter> Parameters { get; set; }

        public FunctionImport()
        {
            Parameters = new List<Parameter>();
        }
        public override XElement GenerateXElement(XNamespace ns)
        {
            XElement retval;
            XName xname = ns == null ? this.GetType().Name : ns + this.GetType().Name;
            retval = new XElement(xname);
            retval.Add(new XAttribute("Name", Name));
            if (! string.IsNullOrEmpty(ReturnType))
                retval.Add(new XAttribute("ReturnType", ReturnType));

            foreach (Parameter parameter in Parameters)
            {
                retval.Add(parameter.GenerateXElement(ns));
            }
            return retval;
        }
    }
    public class FunctionResult : EdmUtilBOBase
    {
        public class Property : EdmUtilBOBase
        {
            public const string NODENAME = "Property";

            public string Type { get; set; }
            public string Name { get; set; }
            public string NormalizedName
            {
                get
                {
                    return NormalizeName(Name);
                }
            }

            public bool Nullable { get; set; }
            public int? MaxLength { get; set; }
            public int? Precision { get; set; }

            public override XElement GenerateXElement(XNamespace ns)
            {
                XElement retval;
                XName xname = ns == null ? this.GetType().Name : ns + this.GetType().Name;
                retval = new XElement(xname);
                retval.Add(new XAttribute("Name", NormalizedName));
                retval.Add(new XAttribute("Type", Type));
                retval.Add(new XAttribute("Nullable", Nullable.ToString().ToLower()));
                if (MaxLength.HasValue) 
                    retval.Add(new XAttribute("MaxLength", MaxLength.Value.ToString()));
                if (Precision.HasValue) 
                    retval.Add(new XAttribute("Precision", Precision.Value.ToString()));
                return retval;
            }

            //remove diacritics and spaces
            private string NormalizeName(string inputString)
            {
                //!\\ Warning 'œ' will be replaced with a 'o' not an 'oe'
                String normalizedString = inputString.Trim().Normalize(NormalizationForm.FormD);
                normalizedString = normalizedString.Replace('\t', '_');
                normalizedString = normalizedString.Replace(' ', '_');
                StringBuilder stringBuilder = new StringBuilder();
                for (int i = 0; i < normalizedString.Length; i++)
                {
                    Char c = normalizedString[i];
                    if (System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c) != System.Globalization.UnicodeCategory.NonSpacingMark)
                        stringBuilder.Append(c);
                }
                return stringBuilder.ToString();
            }

        }

        public const string NODENAME = "ComplexType";
        public string Name { get; set; }
        public List<Property> Properties { get; set; }

        public FunctionResult()
        {
            Properties = new List<Property>();
        }

        public override XElement GenerateXElement(XNamespace ns)
        {
            XElement retval;
            XName xname = ns == null ? NODENAME : ns + NODENAME;
            retval = new XElement(xname);
            retval.Add(new XAttribute("Name", Name));

            foreach (Property property in Properties)
            {
                retval.Add(property.GenerateXElement(ns));
            }
            return retval;
        }
    }
    public class FunctionImportMapping : EdmUtilBOBase
    {
        public class ResultMapping : EdmUtilBOBase
        {
            public class ComplexTypeMapping : EdmUtilBOBase
            {
                public class ScalarProperty : EdmUtilBOBase
                {
                    public string Name { get; set; }
                    public string ColumnName { get; set; }

                    public override XElement GenerateXElement(XNamespace ns)
                    {
                        XElement retval;
                        XName xname = ns == null ? this.GetType().Name.ToString() : ns + this.GetType().Name.ToString();
                        retval = new XElement(xname);
                        retval.Add(new XAttribute("Name", Name));
                        retval.Add(new XAttribute("ColumnName", ColumnName));
                        return retval;
                    }
                }

                public string TypeName { get; set; }
                public List<ScalarProperty> ScalarProperties { get; set; }

                public ComplexTypeMapping(FunctionResult functionResult)
                {
                    ScalarProperties = new List<ScalarProperty>();
                    foreach (FunctionResult.Property property in functionResult.Properties)
                    {
                        ScalarProperties.Add(new ScalarProperty()
                        {
                            Name = property.NormalizedName,
                            ColumnName = property.Name
                        });
                    }

                }

                public override XElement GenerateXElement(XNamespace ns)
                {
                    XElement retval;
                    XName xname = ns == null ? this.GetType().Name.ToString() : ns + this.GetType().Name.ToString();
                    retval = new XElement(xname);
                    retval.Add(new XAttribute("TypeName", TypeName));

                    foreach (ScalarProperty scalarProperty in ScalarProperties)
                        retval.Add(scalarProperty.GenerateXElement(ns));

                    return retval;
                }
            }
            public ComplexTypeMapping ComplexTypeMappingItem { get; set; }
            public ResultMapping(string conceptualModelsSchemaNameSpace, string spName, FunctionResult functionResult)
            {
                this.ComplexTypeMappingItem = new ComplexTypeMapping(functionResult)
                {
                    TypeName = string.Format(Constants.FUNCTIONIMPORTMAPPING_TYPENAME, conceptualModelsSchemaNameSpace, spName)
                };
            }

            public override XElement GenerateXElement(XNamespace ns)
            {
                XElement retval;
                XName xname = ns == null ? this.GetType().Name.ToString() : ns + this.GetType().Name.ToString();
                retval = new XElement(xname);
                retval.Add(ComplexTypeMappingItem.GenerateXElement(ns));

                return retval;
            }
        }

        public string FunctionImportName { get; set; }
        public string FunctionName { get; set; }
        public ResultMapping ResultMappingItem { get; set; }

        public FunctionImportMapping(string conceptualModelsSchemaNameSpace, string spName, FunctionResult functionResult)
        {
            this.ResultMappingItem = null;
            if (functionResult != null)
                this.ResultMappingItem = new ResultMapping(conceptualModelsSchemaNameSpace, spName, functionResult);
        }

        public override XElement GenerateXElement(XNamespace ns)
        {
            XElement retval;
            XName xname = ns == null ? this.GetType().Name.ToString() : ns + this.GetType().Name.ToString();
            retval = new XElement(xname);

            retval.Add(new XAttribute("FunctionImportName", FunctionImportName));
            retval.Add(new XAttribute("FunctionName", FunctionName));

            if (ResultMappingItem != null) 
                retval.Add(ResultMappingItem.GenerateXElement(ns));

            return retval;
        }
    }
}
